Skip to content

docs(skills): drop the issue id from the retirement guidance prescription - #15695

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-15388-retirement-guidance-no-issue-id
Sep 5, 2026
Merged

docs(skills): drop the issue id from the retirement guidance prescription#15695
os-zhuang merged 1 commit into
mainfrom
claude/issue-15388-retirement-guidance-no-issue-id

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #15388

Convention 2 of the retirement playbook prescribed an internal issue id inside the
retiredKey() tombstone string. An author following it literally writes that id into
customer-facing spec text, which check:doc-authoring refuses — so the playbook's own
prescription could not be followed and stay green. The tombstone-note template a few
lines up carried the same fragment.

⚠️ Angle brackets in the quoted strings below are written ⟨thus⟩ so the GitHub body
sanitizer cannot eat them. The Files tab carries the real bytes.

Site 1 — convention 2 of ### guidance 字符串怎么写

before

2. `was removed in @objectstack/spec ⟨version⟩ (#issue[, ADR-XXXX Dn])`。

after

2. `was removed in @objectstack/spec ⟨version⟩ (ADR-XXXX[ Dn])`;`#⟨n⟩` 归 schema 注释,`check:doc-authoring` 把门。

The amended line carries the prescription, the redirect and the gate that enforces it —
and nothing else. #⟨n⟩ is the placeholder spelling the gate itself blesses (its
self-test pins "the #⟨n⟩ placeholder passes while the concrete ids it replaced stay
red"), so the rule displays the very token it forbids without planting one.

Site 2 — the tombstone-note template

before

> `REMOVED ⟨date⟩ (#⟨issue⟩) — tombstoned at the schema (retiredKey carries …

after

> `REMOVED ⟨date⟩ — tombstoned at the schema (retiredKey carries …

Nothing else on that line moves.

Line budget — line-neutral, no ceiling raise

Ratchet verdict lines, verbatim, at 83c73781e:

✓ check-skill-line-ratchet: .claude/skills/spec-property-retirement/SKILL.md is 337 lines (ceiling 337; headroom 0).
✓ check-skill-line-ratchet: .claude/skills/spec-property-retirement/SKILL.md: widest table row is 326 bytes (pin 328; headroom 2).

337 lines before and after; two lines changed, none added. The amended convention line
measures 120 bytes — exactly MAX_LINE_BYTES, which the gate compares with
bytes(line) <= limit. No table row was touched (326 is unchanged, and its
"lower the pin to 326" hint is pre-existing on origin/main, not produced here).

What did not fit, and why it is not here. The dispatch asked the redirect to also
cite packages/spec/src/api/dispatcher.zod.ts:19 and
packages/spec/src/api/plugin-rest-api.zod.ts:39 as the two live examples. With the
prescription code span costing 63 of the 120 bytes, 57 remain; the rule plus the gate
name plus the redirect already spend all 57, and the two paths cost ~60 more. A second
line was the only way to carry them, and every adjacent pair in that list merges to
122–238 bytes, so no line could be paid for by consolidation — and re-wrapping to buy a
line is refused as currency by the 2026-08-17 ruling. The example citations are
illustration rather than rule, so they yielded to the budget; they are recorded here
instead, where reviewers see them and no line ceiling applies.

Census, re-measured on this branch with the grep from the card

grep -rhoE 'was removed (from [^ ]+ )?in @objectstack/spec [0-9.]+ \((#|ADR-)' packages/spec/src --include=*.zod.ts | sort | uniq -c
count
live retiredKey() prescriptions citing an ADR only 87
live prescriptions citing an issue id 0
comment-prose hits citing an issue id 2

Both issue-id hits are comment prose, not prescriptions — dispatcher.zod.ts:19 (a
docblock) and plugin-rest-api.zod.ts:39 (a commented-out example): exactly the place
this change redirects the id to. My ADR total reads 87 where triage read 86; I
record the number I measured rather than adopting theirs, and it does not move the
conclusion — the load-bearing half is that zero live prescriptions carry an id.

One refinement to the card's mechanism

The card says the playbook "is a governed file (check:doc-authoring scans .claude/),
so the edit is checked by the same gate". That holds for the gate's bare-metadata-literal
rule, but not for its issue-id rule, which is scoped to skills/** alone
(PUBLISHED_SKILLS_ROOT = 'skills'); the source says widening it to the internal roots
"would be a different decision needing its own ruling". The gate's own output separates
the two populations:

✓ doc authoring guard: 394 files clean — no bare metadata literals.
✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.

So the playbook was never itself red — the defect is that it prescribed a shape that
goes red at the retiredKey() site where it is applied (the gate's third rule, over
packages/spec customer-facing text). The repair the card asks for is unchanged; only
the reason it is not self-detecting is sharper. This is also why the id survives in the
two comment sites: the gate does not read comments there.

Gates

All 17 families derived by dispatch-gates.mjs --commands --repo objectstack-ai/objectstack
were run on the committed tree 83c73781e; each exit code was captured before any pipe.

✓ dispatch-gates --ran: 17 derived famil(ies) accounted for — 17 run, 0 NOT-MEASURED.

All 17 exited 0. Named verdicts:

✓ doc authoring guard: 14808 customer-facing string(s) across 756 spec sources clean — no internal issue-id references (message 1179 · strictObject 3537 · tombstone 943 · describe 8730 · functionBuilt 254 · functionDeclared 165).
✓ check-skill-line-ratchet — 337 lines, ceiling 337 (above)

node scripts/pm/check-governed-merges.mjs --test .claude/skills/spec-property-retirement/SKILL.md
→ exit 3, GOVERNED: .claude/** ×1. Draft PR, human merge; no seat flips it ready,
enqueues it, or arms auto-merge.

Local suite: packages/spec/src/shared/retired-key-migrate-sentence.test.ts reads this
playbook as one of its two judged corpora — run by path, 14 tests passed, including
"the retirement playbook and the published skill catalog agree with this pin" and
"anti-vacuity: the PLAYBOOK teaches BOTH shapes, so the scan judges at least two sites".
The retirement pin sweep (retirement retired-key tombstone) ran 22 files / 130 tests,
all passed
.

pnpm lint was narrowed rather than run repo-wide, and the narrowing is proven rather
than assumed: eslint's own configuration resolution reports this file as outside its
population — --format json on it returns one result, errorCount: 0, message
"File ignored because no matching configuration was supplied". A markdown file eslint
never parses cannot move any other file's verdict, and no eslint config is in this diff
(1 file changed). CI runs the farm regardless.

Two gates first answered on the environment, not on the tree, and were re-run to a real
reading: check-required-contexts --verify-required-set returned exit 2 NOT VERIFIED
(HTTP 401) until re-run with NODE_OPTIONS=--use-env-proxy → exit 0; and
check:doc-formula-expressions returned exit 3 PREREQUISITE NOT MET until
@objectstack/formula and @objectstack/lint were built → exit 0. Neither was recorded
as a pass while unmeasured.

skip-changeset: this diff publishes nothing from any package — one file under
.claude/**.


Generated by Claude Code

…tion

Convention 2 of the guidance-string conventions prescribed
`(#issue[, ADR-XXXX Dn])`, so an author following it literally wrote an
internal issue id into a `retiredKey()` tombstone — customer-facing spec
text, where `check:doc-authoring` refuses one. The tombstone-note template
a few lines up carried the same `(#<issue>)` fragment.

Convention 2 now prescribes `(ADR-XXXX[ Dn])` and carries the redirect and
the gate that enforces it: the id belongs in the schema comment. The
template drops its fragment. Line-neutral at 337 of 337; the amended
convention is 120 bytes, at the ratchet's per-line cap.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 5, 2026
@claude
claude Bot requested review from hotlong and os-zhuang September 5, 2026 03:16
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 5, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review September 5, 2026 03:23
@os-zhuang
os-zhuang enabled auto-merge September 5, 2026 03:23
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 5, 2026

os-steve commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

ACCEPT — governed in-seat review of PR #15695, head 83c73781e (skills seat, 2026-09-05T03:3xZ). Posted after os-zhuang's approval and enqueue (03:35Z); the review record is owed regardless and changes nothing on the PR.

Implemented-by: claude/issue-15388-retirement-guidance-no-issue-id (os-dev subagent)
Reviewed-by: session_019RfFHiRCSs3JXLK4cwcfox (skills seat)

Verified in a detached compare worktree at the PR head against merge-base 5eb24f867, not from the report: one file, .claude/skills/spec-property-retirement/SKILL.md, two lines changed, none added. Convention 2 now prescribes was removed in @objectstack/spec <version> (ADR-XXXX[ Dn]) and carries the rule and the gate on the same line — the issue id belongs in the schema comment, check:doc-authoring enforces it — at exactly 120 bytes; the tombstone-note template drops its (#<issue>) fragment and nothing else on that line moves. 337 of 337, widest table row 326 of the 328 pin, headroom unchanged; check-governed-merges --test exit 3 (GOVERNED); the dev's check:doc-authoring run is green on both populations, the 17 derived commands all exit 0, retired-key-migrate-sentence.test.ts run by path names the playbook assertions and passes (14 tests), the retirement pin sweep 22 files / 130 tests. CI on 83c73781e: 35 check runs, 24 success, 11 skipped, none failing.

The dev's open question — carry the two live schema-comment examples (dispatcher.zod.ts:19, plugin-rest-api.zod.ts:39) in the file — is ruled A: they stay in the PR body only. The card's ruling scopes the amended lines to the rule and the gate; the redirect already names its destination; a second line at headroom 0 would cost instruction content elsewhere, and re-wrap is not currency for a line. Recorded here, not reopened.

One mechanism refinement from the dev, accepted as the accurate reading: check:doc-authoring's issue-id rule is scoped to skills/** (its bare-metadata rule is what scans .claude/), so the playbook itself was never red — it prescribed a shape that reds at the retiredKey() site where it is applied. The fix is the same either way.

Landing regime: governed (.claude/**); the human step has been taken by an approver. Closes #15388.


Generated by Claude Code

Merged via the queue into main with commit 2ab8cb6 Sep 5, 2026
36 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-15388-retirement-guidance-no-issue-id branch September 5, 2026 04:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec-property-retirement playbook still prescribes an issue id inside the retiredKey() guidance string, which check:doc-authoring now refuses

3 participants